namespace SharpGraphT.Graph;

[Serializable]
public abstract class BaseIntrusiveEdgesSpecifics<TV, TE, TIe>
    where TIe : IntrusiveEdge {
    private static readonly long SerialVersionUid = -7498268216742485L;

    protected IDictionary<TE, TIe> edgeMap;

    public BaseIntrusiveEdgesSpecifics(IDictionary<TE, TIe> edgeMap) =>
        this.edgeMap = edgeMap;

    public bool ContainsEdge(TE e) => edgeMap.ContainsKey(e);

    public IReadOnlySet<TE> EdgeSet => edgeMap.Keys.ToHashSet();

    public void Remove(TE e) => edgeMap.Remove(e);

    public TV GetEdgeSource(TE e) {
        var ie = GetIntrusiveEdge(e);
        if (ie == null) {
            throw new ArgumentException($"no such edge in graph: {e}");
        }

        return (TV) ie.source;
    }

    public TV GetEdgeTarget(TE e) {
        var ie = GetIntrusiveEdge(e);
        if (ie == null) {
            throw new ArgumentException($"no such edge in graph: {e}");
        }

        return (TV) ie.target;
    }

    public virtual double GetEdgeWeight(TE e) => IGraph.DefaultEdgeWeight;

    public virtual void SetEdgeWeight(TE e, double weight) =>
        throw new InvalidOperationException();

    public abstract bool Add(TE e, TV sourceVertex, TV targetVertex);

    protected bool AddIntrusiveEdge(TE edge, TV sourceVertex, TV targetVertex,
        TIe e) {
        if (e.source == null && e.target == null) {
            e.source = sourceVertex;
            e.target = targetVertex;
        } else if (e.source != (object) sourceVertex ||
                   e.target != (object) targetVertex) {
            throw new IntrusiveEdgeException(e.source, e.target);
        }

        return edgeMap.TryAdd(edge, e);
    }

    protected abstract TIe GetIntrusiveEdge(TE e);
}